// Copyright Leonid Shlyapnikov 2010
// Distributed under the GNU Lesser General Public License http://www.gnu.org/licenses/lgpl.html
//    author: Leonid Shlyapnikov, created on: Jan 31, 2010

#include "./MemoryArea.h"

#include <boost/exception/all.hpp>
#include <boost/lexical_cast.hpp>

#include <cstring>
#include <string>
#include <sstream>

#include "./log.h"
#include "./debug.h"


namespace fixparser {

MemoryArea::MemoryArea(int capacity): capacity_(capacity), size_(0), bytes_(new char[capacity]) {
    if(capacity <= 0) {
        BOOST_THROW_EXCEPTION(std::invalid_argument("capacity must be > 0!"));
    }

#ifndef NDEBUG
    std::memset(bytes_, 0, capacity_);
#endif

    LOG_DEBUG("created: " << this->to_string());
}

MemoryArea::~MemoryArea() {
    LOG_DEBUG("deleting " << this->to_string());
    delete[] this->bytes_;
}

void*
MemoryArea::allocate(size_t size) {
    if (size <= 0) {
        BOOST_THROW_EXCEPTION(std::invalid_argument("size must be > 0!"));
    }

    size_t newSize = this->size_ + size;
    if (newSize > this->capacity_) {
        std::stringstream error;
        error << "Not enough capacity in the " << this->to_string() << ", requested size: " << size;
        LOG_ERROR(error.str());
        BOOST_THROW_EXCEPTION(MemoryAreaBadAlloc(error.str()));
    }

    void* result = reinterpret_cast<void*>(this->bytes_ + this->size_);
    this->size_ = newSize;

    return result;
}

// TODO(lshlyapnikov) what is that template<>???
template<>
char*
MemoryArea::allocate<char>(size_t size) {
    return static_cast<char*>(allocate(size));
}

void
MemoryArea::assert_contains(const void* pointer) const {
#ifndef NDEBUG
    using boost::lexical_cast;
    using std::string;

    const bool contains = this->contains(pointer);
    if (false == contains) {
        LOG_ERROR("pointer: " << lexical_cast<string>(pointer) << " is out of range: " << this->to_string());
    }

    assert(contains);
#endif
}

inline
bool
MemoryArea::contains(const void* pointer) const {
    const size_t start = reinterpret_cast<size_t>(bytes_);
    const size_t address = reinterpret_cast<size_t>(pointer);
    const bool result = (start <= address) && (address < (start + size_));

    return result;
}

void
MemoryArea::reset() {
    this->size_ = 0;

#ifndef NDEBUG
    std::memset(bytes_, 0, capacity_);
#endif
}

std::string
MemoryArea::to_string() const {
    using boost::lexical_cast;
    using std::string;

    string result = "MemoryArea@{this: " + lexical_cast<string>(this)
            + ", bytes_: " + lexical_cast<string>(reinterpret_cast<void*>(this->bytes_))
            + ", capacity_: " + lexical_cast<string>(this->capacity_)
            + ", size_: " + lexical_cast<string>(this->size_) + "}";

    return result;
}


}
